%%%--------------------------------------
%%% @Module  : cus_db
%%% @Description: MYSQL数据库操作 
%%%--------------------------------------

-module(cus_mysql).


-compile(export_all).

-include("cus.hrl").
-include("cus_pb.hrl").
-include("ejabberd.hrl").
-include("logger.hrl").


-record(ok_packet, {seq_num :: number(),
		    affected_rows :: number(),
		    insert_id :: number(),
		    status :: number(),
		    warning_count :: number(),
		    msg :: string()
                    | {error, string(), unicode:latin1_chardata() | unicode:chardata() | unicode:external_chardata()}
                    | {incomplete, string(), binary()}}).

% It's unfortunate that error_packet's status is binary when the status of other
% packets is a number.
-record(error_packet, {seq_num :: number(),
		       code :: number(),
		       status :: binary(),
		       msg :: [byte()]}).


-record(result_packet, {seq_num :: number(),
			field_list :: list(),
			rows, extra}).


-define(DB, 'xiaoquan_db').


start(Host, Port, User, Pwd, DB, Encode, Size) ->
    start(?DB, Host, Port, User, Pwd, DB, Encode, Size).

start(PoolId, Host, Port, User, Pwd, DB, Encode, Size) ->
    application:start(emysql),
    emysql:add_pool(PoolId, Size, User, Pwd, Host, Port, DB, Encode),
    ?INFO_MSG("cus mysql : ~p start success", [PoolId]).


remove_pool() ->
    emysql:remove_pool(?DB).

remove_pool(PoolId) ->
    emysql:remove_pool(PoolId),
    ?INFO_MSG("cus mysql : ~p remove success", [PoolId]).


%% 执行一个SQL查询,返回影响的行数
execute(Sql) ->
    execute(?DB, Sql).

execute(PoolId, Sql) ->
    case emysql:execute(PoolId, Sql) of
        #ok_packet{affected_rows = R} ->
            R;
        #error_packet{code = Code, msg = Msg} ->
            mysql_halt(Sql, Code, Msg)
    end.


%% 执行插入语句返回插入ID
insert(Sql) ->
    insert(?DB, Sql).

insert(PoolId, Sql) ->
    case emysql:execute(PoolId, Sql) of
        #ok_packet{insert_id = R} ->
            {ok, R};
        #error_packet{code = Code, msg = Msg} ->
            mysql_halt(Sql, Code, Msg)
    end.


%% 执行分页查询返回结果中的所有行
select_limit(Sql, Offset, Num) ->
    select_limit(?DB, Sql, Offset, Num).

select_limit(PoolId, Sql, Offset, Num) ->
    S = list_to_binary([Sql, <<" limit ">>, integer_to_list(Offset), <<", ">>, integer_to_list(Num)]),
    case emysql:execute(PoolId, S) of
        #result_packet{rows = R} ->
            R;
        #error_packet{code = Code, msg = Msg} ->
            mysql_halt(Sql, Code, Msg)
    end.


%% 取出查询结果中的第一行第一列
%% 未找到时返回null
get_one(Sql) ->
    get_one(?DB, Sql).

get_one(PoolId, Sql) ->
    case emysql:execute(PoolId, Sql) of
        #result_packet{rows = []} ->
            null;
        #result_packet{rows = [[R]]} ->
            R;
        #error_packet{code = Code, msg = Msg} ->
            mysql_halt(Sql, Code, Msg)
    end.
    

%% 取出查询结果中的第一行
get_row(Sql) ->
    get_row(?DB, Sql).

get_row(PoolId, Sql) ->
    case emysql:execute(PoolId, Sql) of
        #result_packet{rows = []} ->
            [];
        #result_packet{rows = [R | _]} ->
            R;
        #error_packet{code = Code, msg = Msg} ->
            mysql_halt(Sql, Code, Msg)
    end.
    

%% 取出查询结果中的所有行
get_all(Sql) ->
    get_all(?DB, Sql).

get_all(PoolId, Sql) ->
    case emysql:execute(PoolId, Sql) of
        #result_packet{rows = R} ->
            R;
        #error_packet{code = Code, msg = Msg} ->
            mysql_halt(Sql, Code, Msg)
    end.


%% @doc 显示人可以看得懂的错误信息
mysql_halt([Sql, Reason]) ->
    erlang:error({db_error, [Sql, Reason]}).


mysql_halt(Sql, Code, Msg) ->
    erlang:error({db_error, [Sql, Code, Msg]}).

